﻿using System;
using System.Threading;
using System.Collections.Generic;

namespace SBPweb.MediaCenter.Modules.Downloader.Torrent
{

    public class TorrentManager
    {

        #region Private / protected fields

        private Dictionary<ITorrentFile, MonoTorrent.Client.TorrentManager> managers = new Dictionary<ITorrentFile, MonoTorrent.Client.TorrentManager>();

        private string savePath;
        private int listenPort = 60960;
        private int uploadSlots = 5;
        private int maxConnections = 10;
        private int maxDownloadSpeed = -1;
        private int maxUploadSpeed = -1;
        private bool fastResumeEnabled = true;

        private MonoTorrent.Client.ClientEngine engine;

        private MonoTorrent.Client.TorrentSettings torrentSettings;
        private MonoTorrent.Client.EngineSettings engineSettings;

        private Thread monitoringThread;
        private bool monitoringExit = false;

        #endregion

        #region Constructor logic

        internal TorrentManager()
        {
            //InitializeSettings();
            InitializeMonitoringThread();
        }

        #endregion

        #region Property accessors

        public string SavePath
        {
            get
            {
                return savePath;
            }
            set
            {
                savePath = value;
                InitializeEngine();
            }
        }

        public int ListenPort
        {
            get
            {
                return listenPort;
            }
            set
            {
                listenPort = value;
                InitializeEngine();
            }
        }

        public int UploadSlots
        {
            get
            {
                return uploadSlots;
            }
            set
            {
                uploadSlots = value;
                InitializeEngine();
            }
        }

        #endregion

        #region Methods

        public void AddTorrent(ITorrentFile file)
        {
            MonoTorrent.Common.Torrent torrent = new MonoTorrent.Common.Torrent();
            torrent.LoadTorrent(file.Path);
            AddToManagers(file, torrent);
        }

        public void RemoveTorrent(ITorrentFile file)
        {
            RemoveFromManagers(file);
        }

        public void StartTorrent(ITorrentFile file)
        {
            MonoTorrent.Client.TorrentManager manager = GetManager(file);
            engine.Start(manager);
        }

        public void StopTorrent(ITorrentFile file)
        {
            MonoTorrent.Client.TorrentManager manager = GetManager(file);
            if (manager.State != MonoTorrent.Common.TorrentState.Stopped)
            {
                engine.Stop(manager);
            }
        }

        public void PauseTorrent(ITorrentFile file)
        {
            MonoTorrent.Client.TorrentManager manager = GetManager(file);
            engine.Pause(manager);
        }

        #region Helpers
        
        public void InitializeEngine()
        {
            InitializeEngine(savePath, listenPort, uploadSlots, maxConnections, maxDownloadSpeed, maxUploadSpeed, fastResumeEnabled);
        }

        public void InitializeEngine(string savePath, int listenPort, int uploadSlots, int maxConnections, int maxDownloadSpeed, int maxUploadSpeed, bool fastResumeEnabled)
        {
            torrentSettings = new MonoTorrent.Client.TorrentSettings(
                this.uploadSlots = uploadSlots, 
                this.maxConnections = maxConnections,
                this.maxDownloadSpeed = maxDownloadSpeed,
                this.maxUploadSpeed = maxUploadSpeed,
                this.fastResumeEnabled = fastResumeEnabled);
            engineSettings = new MonoTorrent.Client.EngineSettings(
                this.savePath = savePath,
                this.listenPort = listenPort);
            engine = new MonoTorrent.Client.ClientEngine(engineSettings, torrentSettings);
        }

        public void FinalizeEngine()
        {
            //WaitHandle[] handles = engine.Stop();
            monitoringExit = true;
            //WaitHandle.WaitAll(handles);
            //engine.Dispose();
            EndMonitoringThread();
        }

        private void AddToManagers(ITorrentFile file, MonoTorrent.Common.Torrent torrent)
        {
            lock (managers)
            {
                if (!managers.ContainsKey(file))
                {
                    managers.Add(file, CreateNewTorrentManager(torrent));
                }
                else
                {
                    managers[file] = CreateNewTorrentManager(torrent);
                }
            }
        }

        private void RemoveFromManagers(ITorrentFile file)
        {
            lock (managers)
            {
                if (managers.ContainsKey(file))
                {
                    lock (managers)
                    {
                        managers.Remove(file);
                    }
                }
            }
        }

        private MonoTorrent.Common.Torrent GetTorrent(ITorrentFile file)
        {
            lock (managers)
            {
                if (managers.ContainsKey(file))
                {
                    return managers[file].Torrent;
                }
                return null;
            }
        }

        private MonoTorrent.Client.TorrentManager GetManager(ITorrentFile file)
        {
            lock (managers)
            {
                if (managers.ContainsKey(file))
                {
                    return managers[file];
                }
                return null;
            }
        }

        private MonoTorrent.Client.TorrentManager CreateNewTorrentManager(MonoTorrent.Common.Torrent torrent)
        {
            return new MonoTorrent.Client.TorrentManager(torrent, savePath, torrentSettings, engineSettings);
        }

        #endregion

        #region Monitoring

        private void InitializeMonitoringThread()
        {
            ThreadStart ts = new ThreadStart(StartMonitoringThread);
            monitoringThread = new Thread(ts);

            monitoringThread.Start();
        }

        private void StartMonitoringThread()
        {
            while (!monitoringExit)
            {
                foreach (MonoTorrent.Client.TorrentManager manager in managers.Values)
                {
                    if (manager.State != MonoTorrent.Common.TorrentState.Stopped && manager.State != MonoTorrent.Common.TorrentState.Paused)
                    {
                        double p = manager.Progress;
                    }
                }
                Thread.Sleep(1000);
            }
        }

        private void EndMonitoringThread()
        {
            monitoringThread.Join();
        }

        #endregion

        #endregion

    }

}